package com.hww.common.util;

import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;

import android.annotation.SuppressLint;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission;

import com.hww.common.MyAppThis;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;

public final class CrashUtils {

    private static String defaultDir;
    private static String dir;
    private static String versionName;
    private static int versionCode;

    private static final String FILE_SEP = System.getProperty("file.separator");
    @SuppressLint("SimpleDateFormat")
    private static final Format FORMAT = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS");

    private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER;
    private static final UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER;

    private static OnCrashListener sOnCrashListener;

    static {
        try {
            PackageInfo pi = MyAppThis.getThis()
                    .getPackageManager()
                    .getPackageInfo(MyAppThis.getThis().getPackageName(), 0);
            if (pi != null) {
                versionName = pi.versionName;
                versionCode = pi.versionCode;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();

        UNCAUGHT_EXCEPTION_HANDLER = new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(final Thread t, final Throwable e) {
                if (e == null) {
                    if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {
                        DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, null);
                    } else {
//                        android.os.Process.killProcess(android.os.Process.myPid());
//                        System.exit(1);
                    }
                    return;
                }

                final String time = FORMAT.format(new Date(System.currentTimeMillis()));
                final StringBuilder sb = new StringBuilder();
                final String head = "************* Log Head ****************" +
                        "\nTime Of Crash      : " + time +
                        "\nDevice Manufacturer: " + Build.MANUFACTURER +
                        "\nDevice Model       : " + Build.MODEL +
                        "\nAndroid Version    : " + Build.VERSION.RELEASE +
                        "\nAndroid SDK        : " + Build.VERSION.SDK_INT +
                        "\nApp VersionName    : " + versionName +
                        "\nApp VersionCode    : " + versionCode +
                        "\nMac  eth0  : " + MacUtil.getMacFormat("eth0") +
                        "\nMac  wlan0  : " + MacUtil.getMacFormat("wlan0") +
                        "\n************* Log Head ****************\n\n";
                sb.append(head);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                e.printStackTrace(pw);
                Throwable cause = e.getCause();
                while (cause != null) {
                    cause.printStackTrace(pw);
                    cause = cause.getCause();
                }
                pw.flush();
                sb.append(sw.toString());
                final String crashInfo = sb.toString();
                /*ApiFaceLog.getInstance().saveErrorLog(StatusEnum.ErrorLogType.CRASH, crashInfo);*/

                final String fullPath = (dir == null ? defaultDir : dir) + "crash_" + time + ".txt";
                if (FileIOUtils.createOrExistsFile(fullPath)) {
                    FileIOUtils.writeFileFromString(fullPath, crashInfo);
                } else {
                    Log.e("CrashUtils", "create " + fullPath + " failed!");
                }

                if (sOnCrashListener != null) {
                    sOnCrashListener.onCrash(crashInfo, e);
                }

                if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {
                    DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, e);
                }
            }
        };
    }

    private CrashUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 初始化崩溃信息收集功能。
     * <p>Must hold {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />}</p>
     */
    @RequiresPermission(WRITE_EXTERNAL_STORAGE)
    public static void init() {
        init("");
    }

    /**
     * 初始化崩溃信息收集功能。
     * <p>Must hold {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />}</p>
     *
     * @param crashDir 崩溃信息的保存文件对象。
     */
    @RequiresPermission(WRITE_EXTERNAL_STORAGE)
    public static void init(@NonNull final File crashDir) {
        init(crashDir.getAbsolutePath(), null);
    }

    /**
     * 初始化崩溃信息收集功能。
     * <p>Must hold {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />}</p>
     *
     * @param crashDirPath 崩溃信息的保存目录路径。如果路径末尾没有文件分隔符，将自动添加。
     */
    @RequiresPermission(WRITE_EXTERNAL_STORAGE)
    public static void init(final String crashDirPath) {
        init(crashDirPath, null);
    }

    /**
     * 初始化崩溃信息收集功能。
     * <p>Must hold {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />}</p>
     *
     * @param onCrashListener 崩溃发生时的监听器，用于执行自定义的操作，例如发送崩溃报告。
     */
    @RequiresPermission(WRITE_EXTERNAL_STORAGE)
    public static void init(final OnCrashListener onCrashListener) {
        init("", onCrashListener);
    }


    /**
     * 初始化崩溃信息收集功能。
     * <p>Must hold {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />}</p>
     *
     * @param onCrashListener 崩溃发生时的监听器，用于执行自定义的操作，例如发送崩溃报告。
     */
    @RequiresPermission(WRITE_EXTERNAL_STORAGE)
    public static void init(@NonNull final File crashDir, final OnCrashListener onCrashListener) {
        init(crashDir.getAbsolutePath(), onCrashListener);
    }


    /**
     * 初始化崩溃信息收集功能。
     * 此方法用于设置当应用程序崩溃时，崩溃信息的保存目录和监听器。必须在AndroidManifest.xml文件中声明android.permission.WRITE_EXTERNAL_STORAGE权限，以允许应用程序写入外部存储器。
     *
     * @param crashDirPath    崩溃信息的保存目录路径。如果路径末尾没有文件分隔符，将自动添加。
     * @param onCrashListener 崩溃发生时的监听器，用于执行自定义的操作，例如发送崩溃报告。
     */
    @RequiresPermission(WRITE_EXTERNAL_STORAGE)
    public static void init(final String crashDirPath, final OnCrashListener onCrashListener) {
        if (StringUtils.isSpace(crashDirPath)) {
            dir = null;
        } else {
            dir = crashDirPath.endsWith(FILE_SEP) ? crashDirPath : crashDirPath + FILE_SEP;
        }
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                && MyAppThis.getThis().getExternalCacheDir() != null) {
            /* /storage/emulated/0/Android/data/<your_package_name>/cache */
            defaultDir = MyAppThis.getThis().getExternalCacheDir() + FILE_SEP + "crash" + FILE_SEP;
        } else {
            /* /data/data/<your_package_name>/cache */
            defaultDir = MyAppThis.getThis().getCacheDir() + FILE_SEP + "crash" + FILE_SEP;
        }
        sOnCrashListener = onCrashListener;
        Thread.setDefaultUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
    }

    public interface OnCrashListener {
        void onCrash(String crashInfo, Throwable e);
    }
}
